package net.oktaeder.office;

import net.oktaeder.office.simulation.Simulation;
import net.oktaeder.office.utils.ordertransformer.OrderTransformer;
import net.oktaeder.office.view.MailDaemon;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.xml.DOMConfigurator;

/**
 * Main class and the entry point for application
 * 
 * @author tuukka
 *
 */
public class MainApplication {

	private MailDaemon mailDaemon;
	private Simulation simulation;
	private OrderTransformer orderTransformer;
	private boolean stillActive;
	private Configuration config;
	private int affinity;
	
	private static Log log = LogFactory.getLog(MainApplication.class);
	private static MainApplication application;
	
	/**
	 * Private constructor used to implement Singleton pattern
	 */
	private MainApplication() {
		//nothing to do here
	}
	
	/**
	 * First method to be called on the system. Creates an instance
	 * of MainApplication and manages it (start up, run, shutdown).
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		application = MainApplication.getInstance();
		if (application.start()) {
			application.run();
		}		
		application.shutdown();
		//TODO: add logging here?
	}

	/**
	 * Starts up the system. Initializes the main components and
	 * starts them properly. After calling this method succesfully
	 * the system is in a state where it can be used to run a simulation
	 * by calling run method of MainApplication.
	 * 
	 * @return true if start up went smoothly, otherwise false
	 */
	private boolean start() {
		try {
			configureLogging();
			log.info("Main application starting up");
			mailDaemon = MailDaemon.getInstance();
			orderTransformer = new OrderTransformer();
			config = ConfigurationHelper.getConfiguration();
			affinity = config.getInt("system.affinity[@amount]");
			simulation = Simulation.getInstance();
			stillActive = true;
			return true;
		} catch (Exception ex) {
			log.fatal("Start up failed, shutting down", ex);
			stillActive = false;
			return false;
		}
	}
	
	/**
	 * Runs a single cycle of simulation. Essentially this method
	 * makes MailDaemon to update it's status, processes the mails and
	 * then sleeps for pre-determined time.
	 */
	private void run() {
		
		while (stillActive) {
			try {
				// update internal mail queue
				mailDaemon.update();
				// transform and dispatch orders 
				orderTransformer.transformMails();
				// handle personal mails
				// handle system mails
				simulation.handleSystemMails();
				Thread.sleep(affinity * 1000);
			} catch (InterruptedException ex) {
				log.trace("Interupted while sleeping, resuming normal course of action");
			} catch (Exception ex) {
				// Catch and log all errors and try to continue running the system
				log.error("Exception occurred", ex);
			}
		}
		//retrieve new mails (from MailDaemon)
		//  parse them into orders
		//  hand out the orders to respective persons
		//  execute immediate action orders (retrieving reports and such)
		//  repeat until "carry on" mail arrives
		//run 1 week of simulation
		//  generate reports and emails
		//keep repeating from beginning until "quit" mail arrives
	}
	
	/**
	 * Shuts down the application.
	 *
	 */
	private void shutdown() {
		//TODO: perform gracious shutdown
		log.info("Main application shutting down");
		stillActive = false;
		//TODO: add error handling here?
	}
	
	/**
	 * Configures the logging framework. Currently just
	 * loads configuration from logging-config.xml file.
	 *
	 */
	private void configureLogging() {
		//TODO: better error handling?
		DOMConfigurator.configure("logging-config.xml");
	}
	
	/**
	 * Signals the main application that it needs to shutdown.
	 * This doesn't cause immediate shutdown, but just schedules
	 * it to be performed after the current run method execution
	 * has finished.
	 */
	public void signalShutDown() {
		log.info("shutdown signaled, system is going down");
		stillActive = false;
	}
	
	/**
	 * Used to retrieve the instance of MainApplication class.
	 * MainApplication has been written as a singleton, so only
	 * zero or one instances can exist at any given moment.
	 * 
	 * @return instance of the main application
	 */
	public synchronized static MainApplication getInstance() {
		try {
			if (application == null) {
				application = new MainApplication();
			}
			return application;
		} catch (Exception ex) {
			log.fatal("Failed to get instance of main application", ex);
			System.exit(-1);
			return null;
		}
	}
}